﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using ParserGenerator.Collections;
using ParserGenerator.Extensions;
using ParserGenerator.GrammarConstruction;

namespace ParserGenerator.ParserConstruction
{
    [Serializable]
    public sealed class ParserAutomatonItemTable : ReadOnlyCollection<ParserAutomatonItem>
    {
        public ReadOnlySet<ParserAutomatonItem> ItemsSeeLookaheadSet { get; }

        internal ParserAutomatonItemTable(GrammarProductionTable productions)
            : base(new List<ParserAutomatonItem>())
        {
            if (productions == null)
                throw new ArgumentNullException(nameof(productions));
            ComputeItems(productions);
            ItemsSeeLookaheadSet = ComputeItemsSeeLookaheadSet();
        }

        private void ComputeItems(GrammarProductionTable productions)
        {
            if (productions == null)
                throw new ArgumentNullException(nameof(productions));
            foreach (GrammarProduction prod in productions)
            {
                for (int i = 0; i <= prod.Length; i++)
                {
                    ParserAutomatonItem item = new ParserAutomatonItem();
                    item.Number = Items.Count;
                    item.Table = this;
                    item.Production = prod;
                    item.DotPosition = i;
                    Items.Add(item);
                    if (i == 0)
                        prod.FirstItem = item;
                }
            }
        }

        private ReadOnlySet<ParserAutomatonItem> ComputeItemsSeeLookaheadSet()
        {
            var set = new HashSet<ParserAutomatonItem>();
            int i = Items.Count - 1;
            while (i-- > 0)
            {
                while (i > 0 && Items[i].IsGoto && Items[i].Symbol.Nullable)
                    set.Add(Items[i--]);
                if (i > 0 && Items[i].IsGoto)
                    set.Add(Items[i--]);
                while (i > 0 && Items[i].IsRead)
                    i--;
            }
            return set.AsReadOnly();
        }

        public sealed override string ToString()
        {
            return string.Join("\n", Items);
        }
    }
}
